package com.newly.center.sys.service.tenant.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.newly.center.sys.service.auth.api.IAuthService;
import com.newly.center.sys.service.authEntity.api.IAuthEntityService;
import com.newly.center.sys.service.role.api.IRoleService;
import com.newly.center.sys.service.tenant.api.ITenantService;
import com.newly.common.base.constants.AuthConstant;
import com.newly.common.base.constants.CodeConstant;
import com.newly.common.base.constants.CommonConstant;
import com.newly.common.base.entity.PageUtils;
import com.newly.common.base.entity.auth.ICurrentTenant;
import com.newly.common.base.entity.business.Po.Site;
import com.newly.common.base.entity.sys.tenant.bo.SimpleTenantBo;
import com.newly.common.base.entity.sys.tenant.dto.*;
import com.newly.common.base.entity.sys.tenant.po.TenantPo;
import com.newly.common.base.entity.sys.tenant.vo.UpdTenantVo;
import com.newly.common.base.entity.sys.tenant.vo.TenantVo;
import com.newly.common.base.exception.NewlyDBException;
import com.newly.common.base.exception.NewlyException;
import com.newly.common.core.utils.CacheManager;
import com.newly.common.core.utils.ConverseUtils;
import com.newly.common.core.utils.RequestUtils;
import com.newly.common.mapper.business.SiteMapper;
import com.newly.transfer.redis.RedisTransfer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author gjm guan
 * @date 2021/8/19 17:18
 * @desc 对用户接口实现
 */
@Service
@Slf4j
public class TenantService extends AbstractTenantService implements ITenantService {

    @Autowired
    private RedisTransfer redisTransfer;

    @Autowired
    private RequestUtils requestUtils;

    @Autowired
    private IAuthEntityService authEntityService;

    @Autowired
    private IAuthService authService;

    @Autowired
    private SiteMapper siteMapper;

    @Autowired
    private IRoleService roleService;

    @Transactional
    @Override
    public boolean save(TenantDto tenantDto) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();

        tenantDto.setCreateBy(currentTenant.getUserId());
        tenantDto.setType(AuthConstant.UserType.USER.getCode());
        Long userId = this.insert(tenantDto);
        Assert.notNull(userId, "Failed to create tenant!");

        // 绑定至权限实体中
        boolean result = authEntityService.bind(AuthConstant.AuthEntityType.TENANT,
                currentTenant.getSelfRoleId(), userId.toString());
        Assert.isTrue(result, "Binding permission error!");

        return authService.pushAuthPayloads(AuthConstant.AuthEntityType.TENANT, userId.toString());
    }

    @Override
    public boolean removeByIdsInAuth(List<Long> tenantIds) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        currentTenant.assertIncludeEntities(AuthConstant.AuthEntityType.TENANT,
                ConverseUtils.toString(tenantIds));
        return removeByIds(tenantIds);
    }

    @Override
    public boolean removeByIdInAuth(Long tenantId) {
        return removeByIdsInAuth(Arrays.asList(tenantId));
    }

    @Override
    public boolean removeByIds(List<Long> tenantIds) {
        if (ObjectUtil.isEmpty(tenantIds)) return false;
        int result = tenantMapper.deleteBatchIds(tenantIds);
        return result > 0;
    }

    @Override
    public boolean removeById(Long tenantId) {
        return removeByIds(Arrays.asList(tenantId));
    }

    @Override
    public SimpleTenantBo getSimpleTenant(Long tenantId) {
        // 尝试从redis中获取
        SimpleTenantDto simpleTenantDto = redisTransfer.getSimpleTenant(tenantId);
        if (!ObjectUtil.isEmpty(simpleTenantDto)) {
            return BeanUtil.copyProperties(simpleTenantDto, SimpleTenantBo.class);
        }
        // 数据库中获取并存储一份至redis中
        TenantPo tenantPo = this.getById(tenantId);
        SimpleTenantBo simpleTenantBo = null;
        if (!ObjectUtil.isEmpty(tenantPo)) {
            simpleTenantBo = new SimpleTenantBo(tenantPo);
            simpleTenantDto = BeanUtil.copyProperties(simpleTenantBo, SimpleTenantDto.class);
            redisTransfer.setSimpleTenant(tenantId, simpleTenantDto);
        }
        return simpleTenantBo;
    }

    @Override
    public List<TenantPo> getByIds(List<Long> tenantIds) {
        List<TenantPo> tenantPos = this.selectByIds(tenantIds);
        return tenantPos;
    }

    @Override
    public TenantPo getById(Long tenantId) {
        List<TenantPo> tenantPos = this.getByIds(Arrays.asList(tenantId));
        if (!ObjectUtil.isEmpty(tenantPos)) {
            return tenantPos.get(0);
        }
        return null;
    }

    @Override
    public TenantPo getLoginTenant(String username, AuthConstant.UserType type) {
        TenantPo tenantPo = null;
        try {
            tenantPo = this.selectByUsernameAndType(username, type.getCode());
        } catch (Exception e) {
            log.error("The tenant not unique: " + e);
        }
        return tenantPo;
    }

    @Override
    public TenantPo getCurrentTenant() {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        return getById(currentTenant.getUserId());
    }

    @Override
    public PageUtils listInAuth(Integer offset, Integer limit, Integer domainId,
                                String fromTime, String toTime, String keyword) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        List<String> entityIds = currentTenant.getEntityIds(AuthConstant.AuthEntityType.TENANT);
        if (ObjectUtil.isEmpty(entityIds)) return null;

        LambdaQueryWrapper<TenantPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(TenantPo::getId, entityIds);
        // 区域id
        if (!ObjectUtil.isEmpty(domainId)) {
            currentTenant.assertIncludeEntity(AuthConstant.AuthEntityType.DOMAIN,
                    domainId.toString());
            wrapper.eq(TenantPo::getDomainId, domainId);
        }
        // 创建时间段
        if (!StrUtil.isBlank(fromTime) && !StrUtil.isBlank(toTime)) {
            wrapper.between(TenantPo::getCreateTime, fromTime, toTime);
        }
        // 关键字
        if (!StrUtil.isBlank(keyword)) {
            wrapper.like(TenantPo::getUsername, keyword);
        }

        Page<TenantPo> page = new Page<>(offset, limit);
        page = tenantMapper.selectPage(page, wrapper);

        return new PageUtils(page);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public boolean save(CreateTenantDto tenantDto) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();

        // 角色必须绑定是当前租户拥有的
        if (!ObjectUtil.isEmpty(tenantDto.getRoleId())) {
            currentTenant.assertIncludeRole(tenantDto.getRoleId());
        }

        TenantPo tenantPo = BeanUtil.copyProperties(tenantDto, TenantPo.class);
        // 密码加密
        tenantPo.setPassword(passwordEncoder.encode(tenantPo.getPassword()));
        DateTime currentDateTime = DateUtil.date();
        tenantPo.setCreateTime(currentDateTime);
        tenantPo.setUpdateTime(currentDateTime);
        tenantPo.setDel(CommonConstant.DeleteFlag.NO_DELETE.getCode());
        tenantPo.setType(AuthConstant.UserType.ADMIN.getCode());
        int result = tenantMapper.insert(tenantPo);

        // 将租户绑定到权限实体中
        boolean bind = authEntityService.bind(AuthConstant.AuthEntityType.TENANT,
                tenantPo.getId().toString());
        Assert.isTrue(bind, "Failed to tenant binding permission!");

        // 租户与角色绑定
        if (!ObjectUtil.isEmpty(tenantDto.getRoleId())) {
            bind = roleService.tenantBindRole(tenantPo.getId().toString(), tenantDto.getRoleId().toString());
            Assert.isTrue(bind, "Failed to role binding permission!");
        }

        return result > 0;
    }

    @Override
    public boolean removeTenant(List<Long> tenantIds) {
        if (ObjectUtil.isEmpty(tenantIds)) return false;

        int result = 0;
        try {
            // 删除租户
            result = tenantMapper.deleteBatchIds(tenantIds);
        } catch (Exception e) {
            throw new NewlyDBException("用户移除库失败！");
        }

        return result > 0;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean logicDel(List<Long> tenantIds) {
        Assert.notEmpty(tenantIds, "tenantIds不能为空！");
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        if (!currentTenant.isDev()) {
            currentTenant.assertIncludeEntities(AuthConstant.AuthEntityType.TENANT,
                    ConverseUtils.toString(tenantIds));
        }
        int flag = tenantMapper.update(null,
                Wrappers.lambdaUpdate(TenantPo.class)
                        .set(TenantPo::getDel, CommonConstant.DeleteFlag.DELETED.getCode())
                        .in(TenantPo::getId, tenantIds)
        );
        return flag > 0;
    }

    @Override
    public PageUtils listTenant(ICurrentTenant currentUser, Integer offset, Integer limit, String keyword) {
        IPage<TenantPo> prePage = new Page<>(offset, limit);
        LambdaQueryWrapper<TenantPo> tenantWrapper = new LambdaQueryWrapper<>();

        // 如果不是开发者只能查看自己创建的租户，并且不能查看自己
        if (!currentUser.isDev()) {
            List<String> entityIds = currentUser.getEntityIds(AuthConstant.AuthEntityType.TENANT);
            if (ObjectUtil.isEmpty(entityIds)) return null;
            tenantWrapper.in(TenantPo::getId, entityIds)
                    .ne(TenantPo::getId, currentUser.getUserId());
        }
        tenantWrapper.eq(TenantPo::getDel, CommonConstant.DeleteFlag.NO_DELETE.getCode())
                .orderByAsc(TenantPo::getUsername);

        // 关键字
        if (StrUtil.isNotBlank(keyword)) {
            tenantWrapper.and(wrapper ->
                    wrapper.like(TenantPo::getUsername, keyword).or()
                            .like(TenantPo::getNickname, keyword).or()
                            .like(TenantPo::getPhone, keyword)
            );
        }

        IPage<TenantPo> tenantPoIPage = tenantMapper.selectPage(prePage, tenantWrapper);

        return new PageUtils<>(tenantPoIPage);
    }

    @Override
    public TenantVo parseCurrentTenant(ICurrentTenant currentUser) {
        return BeanUtil.copyProperties(currentUser, TenantVo.class);
    }

    @Override
    public List<TenantPo> getTenantByRoleIds(List<Integer> roleIds) {
        Assert.notNull(roleIds, "roleIds不能为空");

        // 通过角色id获取租户角色间表数据
//        List<TenantRolePo> tenantRolePos = roleService.getTenantRoleByRoleIds(roleIds);
//        Assert.notNull(tenantRolePos, "获取租户角色中间表数据失败");

        // 租户id
//        List<Long> tenantIds = tenantRolePos.stream().map(tenantRolePo -> tenantRolePo.getUserId()).collect(Collectors.toList());
//
//        return this.doGetTenantByIds(tenantIds);
        return null;
    }

    @Override
    public List<TenantPo> getTenantByIds(List<Long> tenantIds) {
        Assert.notNull(tenantIds, "tenantIds不能为空");
        List<TenantPo> tenantPos = null;
        try {
            tenantPos = tenantMapper.selectBatchIds(tenantIds.stream().collect(Collectors.toSet()));
        } catch (Exception e) {
            throw new NewlyDBException("通过id批量查询租户失败");
        }
        return tenantPos;
    }

    @Override
    public UpdTenantVo queryTenantById(Long tenantId) {
        Assert.notNull(tenantId, "tenantId为空，根据id查询租户失败");
        TenantPo tenantPo = tenantMapper.selectById(tenantId);
        if (ObjectUtil.isNull(tenantPo)) {
            return null;
        }
        Site site = siteMapper.selectById(tenantPo.getSiteId());
        String siteName = ObjectUtil.isNull(site) ? null : site.getName();

        UpdTenantVo updTenantVo = BeanUtil.copyProperties(tenantPo, UpdTenantVo.class);
        updTenantVo.setSiteName(siteName);

        return updTenantVo;
    }

    @Override
    public TenantPo getTenantInMapCache(Long tenantId, Map<Long, TenantPo> tenantPoMap) {
        if (ObjectUtil.isEmpty(tenantId))
            return null;

        TenantPo tenantPo = null;

        // 先尝试从tenantPoMap获取
        if (!ObjectUtil.isEmpty(tenantPoMap)) {
            tenantPo = tenantPoMap.get(tenantId);
        }

        // tenantPoMap获取不到再从数据库中获取,并放置tenantPoMap中
        if (ObjectUtil.isEmpty(tenantPo)) {
            List<TenantPo> tenantPos = this.getTenantByIds(Arrays.asList(tenantId));
            if (ObjectUtil.isEmpty(tenantPos))
                return null;
            tenantPo = tenantPos.get(0);

            // 缓存一份进map中
            if (tenantPoMap != null) {
                tenantPoMap.put(tenantPo.getId(), tenantPo);
            }
        }

        return tenantPo;
    }

    /**
     * 通过id获取租户，支持批量
     *
     * @param tenantIds 租户id
     * @return list TenantPo.class
     */
    private List<TenantPo> doGetTenantByIds(List<Long> tenantIds) {
        if (ObjectUtil.isEmpty(tenantIds))
            return null;
        List<TenantPo> tenantPos = null;
        try {
            tenantPos = tenantMapper.selectBatchIds(tenantIds);
        } catch (Exception e) {
            throw new NewlyDBException("查询租户失败：", e);
        }
        return tenantPos;
    }

    @Override
    public List<String> getUserNames() {
        List<TenantPo> tenantPoList = this.tenantMapper.selectList(new QueryWrapper<TenantPo>().lambda().orderByAsc(TenantPo::getId));
        List<String> userNames = tenantPoList.stream().map(TenantPo::getUsername).collect(Collectors.toList());
        return userNames;
    }

    @Override
    public boolean repeat(String userName) {
        TenantPo tenantPo = this.tenantMapper.selectOne(new QueryWrapper<TenantPo>()
                .lambda().eq(TenantPo::getUsername, userName));
        return tenantPo != null;
    }

    @Override
    public List<TenantPo> repeatEmail(String email) {
        List<TenantPo> list = tenantMapper.selectList(
                Wrappers.lambdaQuery(TenantPo.class)
                        .eq(TenantPo::getDel, 0)
                        .eq(TenantPo::getEmail, email)
        );
        return list;
    }

    @Override
    public boolean updTenant(UpdTenantDto updTenantDto) {
        String email = updTenantDto.getEmail();
        if (StrUtil.isNotBlank(email)) {
            List<TenantPo> pos = this.repeatEmail(email);
            List<Long> ids = pos.stream().map(TenantPo::getId).collect(Collectors.toList());

            if (pos.size() > 0 && !ids.contains(Long.parseLong(updTenantDto.getId()))) {
                throw new NewlyException(CodeConstant.FAILED, "该邮箱已被使用");
            }
        }

        TenantPo tenantPo = BeanUtil.copyProperties(updTenantDto, TenantPo.class);
        tenantPo.setUpdateTime(DateUtil.date());
        return tenantMapper.updateById(tenantPo) > 0;
    }

    @Override
    public boolean updPassword(UpdPasswordDto updPasswordDto) {
        String id = updPasswordDto.getId();
        TenantPo tenantPo = tenantMapper.selectById(id);
        Assert.notNull(tenantPo, "无此用户");
        if (passwordEncoder.matches(updPasswordDto.getLastPassword(), tenantPo.getPassword())) {
            String encode = passwordEncoder.encode(updPasswordDto.getPassword());
            int update = tenantMapper.update(null,
                    Wrappers.lambdaUpdate(TenantPo.class)
                            .eq(TenantPo::getId, id)
                            .set(TenantPo::getPassword, encode)
            );
            return update > 0;
        }
        return false;
    }

    @Override
    public boolean updPasswordByEmail(UpdPasswordByEmailDto dto) {
        String emailCaptcha = CacheManager.getInstance().getEmailCaptcha(dto.getEmail());
        if (!dto.getEmailCaptcha().equalsIgnoreCase(emailCaptcha)) {
            throw new NewlyException("验证码错误");
        }
        TenantPo tenantPo = tenantMapper.selectOne(
                Wrappers.lambdaQuery(TenantPo.class)
                        .eq(TenantPo::getEmail, dto.getEmail())
        );
        Assert.notNull(tenantPo, "无此用户");
        String encode = passwordEncoder.encode(dto.getPassword());
        int update = tenantMapper.update(null,
                Wrappers.lambdaUpdate(TenantPo.class)
                        .set(TenantPo::getPassword, encode)
        );
        return update > 0;
    }
}